package vn.co.thn.android.store.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import vn.co.thn.android.store.NSApp;

import com.android.volley.AuthFailureError;
import com.android.volley.Cache;
import com.android.volley.Cache.Entry;
import com.android.volley.NetworkResponse;
import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.StringRequest;

public class NSRequestService {
	private String mURL;
	private ArrayList<NSParam> mParams;
	private String mKey;
	private Class<?> mJsonResult;
	private Object mDataCache;
	@SuppressWarnings("rawtypes")
	private NSRequestServiceCAllBack mNSRequestServiceCAllBack;
	private NSRequestProcessUI mNSRequestProcessUI;
	private NSNetWorkCallBack mNSNetWorkCallBack;

	public <T> NSRequestService(String url, Class<T> clazz, NSRequestServiceCAllBack<T> requestServiceCAllBack) {
		mNSRequestServiceCAllBack = requestServiceCAllBack;
		mNSRequestProcessUI = null;
		mNSNetWorkCallBack = null;
		mDataCache = null;
		mParams = null;
		mURL = url;
		CreateKey();
		mJsonResult = clazz;
	}
	public <T> NSRequestService(String url, Class<T> clazz, NSRequestServiceCAllBack<T> requestServiceCAllBack,NSRequestProcessUI requestProcessUI) {
		mNSRequestServiceCAllBack = requestServiceCAllBack;
		mNSRequestProcessUI = requestProcessUI;
		mNSNetWorkCallBack = null;
		mDataCache = null;
		mParams = null;
		mURL = url;
		CreateKey();
		mJsonResult = clazz;
	}
	public <T> NSRequestService(String url, Class<T> clazz, ArrayList<NSParam> params, NSRequestServiceCAllBack<T> requestServiceCAllBack) {
		mNSRequestServiceCAllBack = requestServiceCAllBack;
		mNSRequestProcessUI = null;
		mNSNetWorkCallBack = null;
		mDataCache = null;
		mParams = params;
		mURL = url;
		CreateKey();
		mJsonResult = clazz;
	}

	public <T> NSRequestService(String url, Class<T> clazz, ArrayList<NSParam> params, NSRequestServiceCAllBack<T> requestServiceCAllBack,
			NSRequestProcessUI requestProcessUI) {
		mNSRequestServiceCAllBack = requestServiceCAllBack;
		mNSRequestProcessUI = requestProcessUI;
		mNSNetWorkCallBack = null;
		mDataCache = null;
		mParams = params;
		mURL = url;
		CreateKey();
		mJsonResult = clazz;
	}

	public <T> NSRequestService(String url, Class<T> clazz, ArrayList<NSParam> params, NSRequestServiceCAllBack<T> requestServiceCAllBack,
			NSRequestProcessUI requestProcessUI, NSNetWorkCallBack netWorkCallBack) {
		mNSRequestServiceCAllBack = requestServiceCAllBack;
		mNSRequestProcessUI = requestProcessUI;
		mNSNetWorkCallBack = netWorkCallBack;
		mDataCache = null;
		mParams = params;
		mURL = url;
		mJsonResult = clazz;
		CreateKey();

	}

	private void CreateKey() {
		if (mParams != null && mParams.size() > 0)
			mKey = mParams.toString();
		else
			mKey = "";
		mKey = mURL + mKey;
		mDataCache = getCache();
	}

	public String getKey() {
		return mKey;
	}

	@SuppressWarnings("unchecked")
	private <T> void Request(int method) {
		if (mNSRequestProcessUI != null) {
			mNSRequestProcessUI.ShowProcess();
		}
		if (mDataCache != null) {
			try {
				if (mDataCache instanceof JSONObject) {
					mNSRequestServiceCAllBack.onResult(NSBaseParse.ParserJsonToObject(mDataCache.toString(), mJsonResult), true);
				} else {
					JSONArray jsonArray = new JSONArray(mDataCache.toString());
					ArrayList<T> lst = new ArrayList<T>();
					for (int i = 0; i < jsonArray.length(); i++) {
						lst.add((T) NSBaseParse.ParserJsonToObject(jsonArray.getJSONObject(i), mJsonResult));
					}
					mNSRequestServiceCAllBack.onResult(lst, true);
				}

			} catch (Exception e) {
				mNSRequestServiceCAllBack.onException(e);
				e.printStackTrace();
			}
		}
		StringRequest stringRequest = new RequestJson(method, mURL, new Listener<String>() {

			@Override
			public void onResponse(String response) {
				try {

					if (mDataCache != null) {
						if (!response.equalsIgnoreCase(mDataCache.toString())) {
							mNSRequestServiceCAllBack.onResult(NSBaseParse.ParserJsonToObject(response, mJsonResult), false);
							NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
						}
					} else {
						if (convetString(response) instanceof JSONObject) {
							mNSRequestServiceCAllBack.onResult(NSBaseParse.ParserJsonToObject(response, mJsonResult), true);
							NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
						} else {
							JSONArray jsonArray = new JSONArray(response);
							ArrayList<T> lst = new ArrayList<T>();
							for (int i = 0; i < jsonArray.length(); i++) {
								lst.add((T) NSBaseParse.ParserJsonToObject(jsonArray.getJSONObject(i), mJsonResult));
							}
							mNSRequestServiceCAllBack.onResult(lst, true);
							NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
						}
					}
					if (mNSRequestProcessUI != null) {
						mNSRequestProcessUI.HideProcess();
					}

				} catch (Exception e) {
					mNSRequestServiceCAllBack.onException(e);
					if (mNSRequestProcessUI != null) {
						mNSRequestProcessUI.HideProcess();
					}
					e.printStackTrace();
				}
				if (mNSNetWorkCallBack != null)
					mNSNetWorkCallBack.NetWorkSuccess();
			}
		}, new ErrorListener() {

			@Override
			public void onErrorResponse(VolleyError error) {
				mNSRequestServiceCAllBack.onError(error);
				if (mNSRequestProcessUI != null) {
					mNSRequestProcessUI.HideProcess();
				}
				if (error.networkResponse == null) {
					if (mDataCache != null) {
						if (mNSNetWorkCallBack != null)
							mNSNetWorkCallBack.NetWorkError(false);
					} else {
						if (mNSNetWorkCallBack != null)
							mNSNetWorkCallBack.NetWorkError(true);
					}
				}
			}
		});
		NSApp.getInstance().addToRequestQueue(stringRequest, mKey);
	}

	private Object convetString(String str) {
		try {
			return new JSONObject(str);
		} catch (JSONException ex) {
			// edited, to include @Arthur's comment
			// e.g. in case JSONArray is valid as well...
			try {
				return new JSONArray(str);
			} catch (JSONException e) {
				return null;
			}
		}
	}

	public <T> void StartMultipart(String Path_file, String file_key) {
		if (mNSRequestProcessUI != null) {
			mNSRequestProcessUI.ShowProcess();
		}
		StringRequest stringRequest = new RequestJson(Method.POST, this.mURL, Path_file, file_key, new Listener<String>() {

			@SuppressWarnings("unchecked")
			@Override
			public void onResponse(String response) {
				// TODO Auto-generated method stub
				try {
					if (convetString(response) instanceof JSONObject) {
						mNSRequestServiceCAllBack.onResult(NSBaseParse.ParserJsonToObject(response, mJsonResult), true);
						NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
					} else {
						JSONArray jsonArray = new JSONArray(response);
						ArrayList<T> lst = new ArrayList<T>();
						for (int i = 0; i < jsonArray.length(); i++) {
							lst.add((T) NSBaseParse.ParserJsonToObject(jsonArray.getJSONObject(i), mJsonResult));
						}
						mNSRequestServiceCAllBack.onResult(lst, true);
						NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
					}
				} catch (Exception e) {
					mNSRequestServiceCAllBack.onException(e);
					if (mNSRequestProcessUI != null) {
						mNSRequestProcessUI.HideProcess();
					}
					e.printStackTrace();
				}
				if (mNSNetWorkCallBack != null)
					mNSNetWorkCallBack.NetWorkSuccess();
			}
		}, new ErrorListener() {

			@Override
			public void onErrorResponse(VolleyError error) {
				// TODO Auto-generated method stub
				mNSRequestServiceCAllBack.onError(error);
				if (mNSRequestProcessUI != null) {
					mNSRequestProcessUI.HideProcess();
				}
				if (error.networkResponse == null) {
					if (mDataCache != null) {
						if (mNSNetWorkCallBack != null)
							mNSNetWorkCallBack.NetWorkError(false);
					} else {
						if (mNSNetWorkCallBack != null)
							mNSNetWorkCallBack.NetWorkError(true);
					}
				}
			}
		});
		NSApp.getInstance().addToRequestQueue(stringRequest, mKey);
	}

	public <T> void StartMultipart(File file, String file_key) {
		if (mNSRequestProcessUI != null) {
			mNSRequestProcessUI.ShowProcess();
		}
		StringRequest stringRequest = new RequestJson(Method.POST, this.mURL, file, file_key, new Listener<String>() {

			@SuppressWarnings("unchecked")
			@Override
			public void onResponse(String response) {
				// TODO Auto-generated method stub
				try {
					if (convetString(response) instanceof JSONObject) {
						mNSRequestServiceCAllBack.onResult(NSBaseParse.ParserJsonToObject(response, mJsonResult), true);
						NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
					} else {
						JSONArray jsonArray = new JSONArray(response);
						ArrayList<T> lst = new ArrayList<T>();
						for (int i = 0; i < jsonArray.length(); i++) {
							lst.add((T) NSBaseParse.ParserJsonToObject(jsonArray.getJSONObject(i), mJsonResult));
						}
						mNSRequestServiceCAllBack.onResult(lst, true);
						NSApp.getInstance().getRequestQueue().getCache().invalidate(mKey, true);
					}
				} catch (Exception e) {
					mNSRequestServiceCAllBack.onException(e);
					if (mNSRequestProcessUI != null) {
						mNSRequestProcessUI.HideProcess();
					}
					e.printStackTrace();
				}
				if (mNSNetWorkCallBack != null)
					mNSNetWorkCallBack.NetWorkSuccess();
			}
		}, new ErrorListener() {

			@Override
			public void onErrorResponse(VolleyError error) {
				// TODO Auto-generated method stub
				mNSRequestServiceCAllBack.onError(error);
				if (mNSRequestProcessUI != null) {
					mNSRequestProcessUI.HideProcess();
				}
				if (error.networkResponse == null) {
					if (mDataCache != null) {
						if (mNSNetWorkCallBack != null)
							mNSNetWorkCallBack.NetWorkError(false);
					} else {
						if (mNSNetWorkCallBack != null)
							mNSNetWorkCallBack.NetWorkError(true);
					}
				}
			}
		});
		NSApp.getInstance().addToRequestQueue(stringRequest, mKey);
	}

	public Object getCache() {
		Cache cache = NSApp.getInstance().getRequestQueue().getCache();
		Entry entry = cache.get(this.mKey);
		if (entry != null) {
			try {
				String data = new String(entry.data, "UTF-8");
				return data;
			} catch (UnsupportedEncodingException e) {
				return null;
			}
		} else {
			return null;
		}
	}

	public synchronized void deleteCache() {
		NSApp.getInstance().getRequestQueue().getCache().remove(this.mKey);
		mDataCache = null;
	}

	public void StartGet() {
		Request(Method.GET);
	}

	public void StartPost() {
		Request(Method.POST);
	}

	public void setRequestProcessUI(NSRequestProcessUI requestProcessUI) {
		this.mNSRequestProcessUI = requestProcessUI;
	}

	public void setNetWorkCallBack(NSNetWorkCallBack netWorkCallBack) {
		this.mNSNetWorkCallBack = netWorkCallBack;
	}

	public interface NSRequestServiceCAllBack<T> {
		public void onResult(T data, boolean isUpdate);

		public void onResult(ArrayList<T> data, boolean isUpdate);

		public void onError(VolleyError error);

		public void onException(Exception e);
	}

	public interface NSRequestProcessUI {
		public void ShowProcess();

		public void HideProcess();
	}

	public interface NSNetWorkCallBack {
		public void NetWorkError(boolean showEmty);

		public void NetWorkSuccess();

	}

	// Request Json
	class RequestJson extends StringRequest {
		private boolean isMultipart = false;
		private HttpEntity mHttpEntity;
		private String path_file;
		private File file;
		private String mFile_Key;

		public RequestJson(int method, String url, Listener<String> listener, ErrorListener errorListener) {
			super(method, url, listener, errorListener);
		}

		public RequestJson(int method, String url, String Path_file, String File_Key, Listener<String> listener, ErrorListener errorListener) {
			super(method, url, listener, errorListener);
			setPathFile(Path_file);
			isMultipart = true;
			mFile_Key = File_Key;
			mHttpEntity = buildMultipartEntity();
		}

		public RequestJson(int method, String url, File file, String File_Key, Listener<String> listener, ErrorListener errorListener) {
			super(method, url, listener, errorListener);
			setFile(file);
			isMultipart = true;
			mFile_Key = File_Key;
			mHttpEntity = buildMultipartEntity();
		}

		@Override
		public String getCacheKey() {
			return mKey;
		}

		@Override
		protected Map<String, String> getParams() throws AuthFailureError {
			Map<String, String> Params = new HashMap<String, String>();
			if (mParams != null) {
				for (NSParam param : mParams) {
					Params.put(param.getParamName().toString(), param.getParamValue().toString());
				}
			}
			return Params;
		}

		@Override
		public String getBodyContentType() {
			if (this.isMultipart) {
				return mHttpEntity.getContentType().getValue();
			}
			return super.getBodyContentType();
		}

		@Override
		public byte[] getBody() throws AuthFailureError {
			if (isMultipart) {
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				try {
					mHttpEntity.writeTo(bos);
				} catch (IOException e) {
					VolleyLog.e("IOException writing to ByteArrayOutputStream");
				}
				return bos.toByteArray();
			}
			return super.getBody();
		}

		@Override
		protected Response<String> parseNetworkResponse(NetworkResponse response) {
			String parsed;
			try {
				parsed = new String(response.data, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				parsed = new String(response.data);
			}
			return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
		}

		private HttpEntity buildMultipartEntity() {
			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			if (this.file != null) {
				FileBody fileBody = new FileBody(file);
				builder.addPart(mFile_Key, fileBody);
			}
			for (NSParam param : mParams) {
				builder.addTextBody(param.getParamName(), param.getParamValue().toString());
			}
			return builder.build();
		}

		public void setMultipart(boolean Multipart) {
			this.isMultipart = Multipart;
		}

		private void setPathFile(String path) {
			this.path_file = path;
			file = new File(path_file);
		}

		private void setFile(File f) {
			this.file = f;
		}
	}
}
